1 package org.apache.maven.surefire.junitcore.pc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.junitcore.JUnitCoreParameters;
23 import org.apache.maven.surefire.testset.TestSetFailedException;
24 import org.junit.runner.Description;
25
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Iterator;
29
30
31
32
33
34
35
36
37
38
39 final class ParallelComputerUtil
40 {
41 private static final Collection<Description> UNUSED_DESCRIPTIONS =
42 Arrays.asList( null, Description.createSuiteDescription( "null" ), Description.TEST_MECHANISM,
43 Description.EMPTY );
44
45 private static int availableProcessors = Runtime.getRuntime().availableProcessors();
46
47 private ParallelComputerUtil()
48 {
49 throw new IllegalStateException( "Suppresses calling constructor, ensuring non-instantiability." );
50 }
51
52
53
54
55 static void overrideAvailableProcessors( int availableProcessors )
56 {
57 ParallelComputerUtil.availableProcessors = availableProcessors;
58 }
59
60
61
62
63 static void setDefaultAvailableProcessors()
64 {
65 ParallelComputerUtil.availableProcessors = Runtime.getRuntime().availableProcessors();
66 }
67
68 static Concurrency resolveConcurrency( JUnitCoreParameters params, RunnerCounter counts )
69 throws TestSetFailedException
70 {
71 if ( !params.isParallelismSelected() )
72 {
73 throw new TestSetFailedException( "Unspecified parameter '" + JUnitCoreParameters.PARALLEL_KEY + "'." );
74 }
75
76 if ( !params.isUseUnlimitedThreads() && !hasThreadCount( params ) && !hasThreadCounts( params ) )
77 {
78 throw new TestSetFailedException( "Unspecified thread-count(s). "
79 + "See the parameters "
80 + JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY + ", "
81 + JUnitCoreParameters.THREADCOUNT_KEY + ", "
82 + JUnitCoreParameters.THREADCOUNTSUITES_KEY + ", "
83 + JUnitCoreParameters.THREADCOUNTCLASSES_KEY + ", "
84 + JUnitCoreParameters.THREADCOUNTMETHODS_KEY + "." );
85 }
86
87 if ( params.isUseUnlimitedThreads() )
88 {
89 return concurrencyForUnlimitedThreads( params );
90 }
91 else if ( hasThreadCount( params ) )
92 {
93 if ( hasThreadCounts( params ) )
94 {
95 return isLeafUnspecified( params )
96 ? concurrencyFromAllThreadCountsButUnspecifiedLeafCount( params, counts )
97 : concurrencyFromAllThreadCounts( params );
98 }
99 else
100 {
101 return estimateConcurrency( params, counts );
102 }
103 }
104 else
105 {
106 return concurrencyFromThreadCounts( params );
107 }
108 }
109
110 static boolean isUnusedDescription( Description examined )
111 {
112 if ( UNUSED_DESCRIPTIONS.contains( examined ) )
113 {
114 return true;
115 }
116 else
117 {
118
119 for ( Description unused : UNUSED_DESCRIPTIONS )
120 {
121 if ( unused != null && unused.getDisplayName().equals( examined.getDisplayName() ) )
122 {
123 return true;
124 }
125 }
126 return false;
127 }
128 }
129
130 static void removeUnusedDescriptions( Collection<Description> examined )
131 {
132 for ( Iterator<Description> it = examined.iterator(); it.hasNext(); )
133 {
134 if ( isUnusedDescription( it.next() ) )
135 {
136 it.remove();
137 }
138 }
139 }
140
141 private static Concurrency concurrencyForUnlimitedThreads( JUnitCoreParameters params )
142 {
143 Concurrency concurrency = new Concurrency();
144 concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0;
145 concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0;
146 concurrency.methods = params.isParallelMethods() ? threadCountMethods( params ) : 0;
147 concurrency.capacity = Integer.MAX_VALUE;
148 return concurrency;
149 }
150
151 private static Concurrency estimateConcurrency( JUnitCoreParameters params, RunnerCounter counts )
152 {
153 final Concurrency concurrency = new Concurrency();
154 final int parallelEntities = countParallelEntities( params );
155 concurrency.capacity = multiplyByCoreCount( params, params.getThreadCount() );
156 if ( parallelEntities == 1 || counts == null || counts.classes == 0 )
157 {
158
159 double ratio = 1d / parallelEntities;
160 int threads = multiplyByCoreCount( params, ratio * params.getThreadCount() );
161 concurrency.suites = params.isParallelSuites() ? minSuites( threads, counts ) : 0;
162 concurrency.classes = params.isParallelClasses() ? minClasses( threads, counts ) : 0;
163 concurrency.methods = params.isParallelMethods() ? minMethods( threads, counts ) : 0;
164 if ( parallelEntities == 1 )
165 {
166 concurrency.capacity = 0;
167 }
168 else
169 {
170 adjustLeaf( params, concurrency );
171 }
172 }
173 else
174 {
175
176 concurrency.suites = params.isParallelSuites() ? toNonNegative( counts.suites ) : 0;
177 concurrency.classes = params.isParallelClasses() ? toNonNegative( counts.classes ) : 0;
178 concurrency.methods =
179 params.isParallelMethods() ? toNonNegative( Math.ceil( counts.methods / (double) counts.classes ) ) : 0;
180 double sum = toNonNegative( concurrency.suites + concurrency.classes + concurrency.methods );
181 if ( concurrency.capacity < sum && sum != 0 )
182 {
183
184 double weight = concurrency.capacity / sum;
185 concurrency.suites *= weight;
186 concurrency.classes *= weight;
187 concurrency.methods *= weight;
188 }
189 adjustLeaf( params, concurrency );
190 }
191 return concurrency;
192 }
193
194 private static Concurrency concurrencyFromAllThreadCountsButUnspecifiedLeafCount( JUnitCoreParameters params,
195 RunnerCounter counts )
196 {
197 Concurrency concurrency = new Concurrency();
198 concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
199 concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.suites ) : 0;
200 concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
201 concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.classes ) : 0;
202 concurrency.methods = params.isParallelMethods() ? params.getThreadCountMethods() : 0;
203 concurrency.methods = params.isParallelMethods() ? multiplyByCoreCount( params, concurrency.methods ) : 0;
204 concurrency.capacity = multiplyByCoreCount( params, params.getThreadCount() );
205
206 if ( counts != null )
207 {
208 concurrency.suites = toNonNegative( Math.min( concurrency.suites, counts.suites ) );
209 concurrency.classes = toNonNegative( Math.min( concurrency.classes, counts.classes ) );
210 }
211
212 setLeafInfinite( params, concurrency );
213
214 return concurrency;
215 }
216
217 private static Concurrency concurrencyFromAllThreadCounts( JUnitCoreParameters params )
218 {
219 Concurrency concurrency = new Concurrency();
220 concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
221 concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
222 concurrency.methods = params.isParallelMethods() ? params.getThreadCountMethods() : 0;
223 concurrency.capacity = params.getThreadCount();
224 double all = sumThreadCounts( concurrency );
225
226 concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.capacity * (
227 concurrency.suites / all ) ) : 0;
228
229 concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.capacity * (
230 concurrency.classes / all ) ) : 0;
231
232 concurrency.methods = params.isParallelMethods() ? multiplyByCoreCount( params, concurrency.capacity * (
233 concurrency.methods / all ) ) : 0;
234
235 concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity );
236 adjustPrecisionInLeaf( params, concurrency );
237 return concurrency;
238 }
239
240 private static Concurrency concurrencyFromThreadCounts( JUnitCoreParameters params )
241 {
242 Concurrency concurrency = new Concurrency();
243 concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0;
244 concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0;
245 concurrency.methods = params.isParallelMethods() ? threadCountMethods( params ) : 0;
246 concurrency.capacity = toNonNegative( sumThreadCounts( concurrency ) );
247 return concurrency;
248 }
249
250 private static int countParallelEntities( JUnitCoreParameters params )
251 {
252 int count = 0;
253 if ( params.isParallelSuites() )
254 {
255 count++;
256 }
257
258 if ( params.isParallelClasses() )
259 {
260 count++;
261 }
262
263 if ( params.isParallelMethods() )
264 {
265 count++;
266 }
267 return count;
268 }
269
270 private static void adjustPrecisionInLeaf( JUnitCoreParameters params, Concurrency concurrency )
271 {
272 if ( params.isParallelMethods() )
273 {
274 concurrency.methods = concurrency.capacity - concurrency.suites - concurrency.classes;
275 }
276 else if ( params.isParallelClasses() )
277 {
278 concurrency.classes = concurrency.capacity - concurrency.suites;
279 }
280 }
281
282 private static void adjustLeaf( JUnitCoreParameters params, Concurrency concurrency )
283 {
284 if ( params.isParallelMethods() )
285 {
286 concurrency.methods = Integer.MAX_VALUE;
287 }
288 else if ( params.isParallelClasses() )
289 {
290 concurrency.classes = Integer.MAX_VALUE;
291 }
292 }
293
294 private static void setLeafInfinite( JUnitCoreParameters params, Concurrency concurrency )
295 {
296 if ( params.isParallelMethods() )
297 {
298 concurrency.methods = Integer.MAX_VALUE;
299 }
300 else if ( params.isParallelClasses() )
301 {
302 concurrency.classes = Integer.MAX_VALUE;
303 }
304 else if ( params.isParallelSuites() )
305 {
306 concurrency.suites = Integer.MAX_VALUE;
307 }
308 }
309
310 private static boolean isLeafUnspecified( JUnitCoreParameters params )
311 {
312 int maskOfParallel = params.isParallelSuites() ? 4 : 0;
313 maskOfParallel |= params.isParallelClasses() ? 2 : 0;
314 maskOfParallel |= params.isParallelMethods() ? 1 : 0;
315
316 int maskOfConcurrency = params.getThreadCountSuites() > 0 ? 4 : 0;
317 maskOfConcurrency |= params.getThreadCountClasses() > 0 ? 2 : 0;
318 maskOfConcurrency |= params.getThreadCountMethods() > 0 ? 1 : 0;
319
320 maskOfConcurrency &= maskOfParallel;
321
322 int leaf = Integer.lowestOneBit( maskOfParallel );
323 return maskOfConcurrency == maskOfParallel - leaf;
324 }
325
326 private static double sumThreadCounts( Concurrency concurrency )
327 {
328 double sum = concurrency.suites;
329 sum += concurrency.classes;
330 sum += concurrency.methods;
331 return sum;
332 }
333
334 private static boolean hasThreadCounts( JUnitCoreParameters jUnitCoreParameters )
335 {
336 return ( jUnitCoreParameters.isParallelSuites() && jUnitCoreParameters.getThreadCountSuites() > 0 )
337 || ( jUnitCoreParameters.isParallelClasses() && jUnitCoreParameters.getThreadCountClasses() > 0 )
338 || ( jUnitCoreParameters.isParallelMethods() && jUnitCoreParameters.getThreadCountMethods() > 0 );
339 }
340
341 private static boolean hasThreadCount( JUnitCoreParameters jUnitCoreParameters )
342 {
343 return jUnitCoreParameters.getThreadCount() > 0;
344 }
345
346 private static int threadCountMethods( JUnitCoreParameters jUnitCoreParameters )
347 {
348 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountMethods() );
349 }
350
351 private static int threadCountClasses( JUnitCoreParameters jUnitCoreParameters )
352 {
353 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountClasses() );
354 }
355
356 private static int threadCountSuites( JUnitCoreParameters jUnitCoreParameters )
357 {
358 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountSuites() );
359 }
360
361 private static int multiplyByCoreCount( JUnitCoreParameters jUnitCoreParameters, double threadsPerCore )
362 {
363 double numberOfThreads =
364 jUnitCoreParameters.isPerCoreThreadCount() ? threadsPerCore * (double) availableProcessors : threadsPerCore;
365
366 return numberOfThreads > 0 ? toNonNegative( numberOfThreads ) : Integer.MAX_VALUE;
367 }
368
369 private static int minSuites( int threads, RunnerCounter counts )
370 {
371 long count = counts == null ? Integer.MAX_VALUE : counts.suites;
372 return Math.min( threads, toNonNegative( count ) );
373 }
374
375 private static int minClasses( int threads, RunnerCounter counts )
376 {
377 long count = counts == null ? Integer.MAX_VALUE : counts.classes;
378 return Math.min( threads, toNonNegative( count ) );
379 }
380
381 private static int minMethods( int threads, RunnerCounter counts )
382 {
383 long count = counts == null ? Integer.MAX_VALUE : counts.methods;
384 return Math.min( threads, toNonNegative( count ) );
385 }
386
387 private static int toNonNegative( long num )
388 {
389 return (int) Math.min( num > 0 ? num : 0, Integer.MAX_VALUE );
390 }
391
392 private static int toNonNegative( double num )
393 {
394 return (int) Math.min( num > 0 ? num : 0, Integer.MAX_VALUE );
395 }
396 }